from gmsh_parser import gmsh_parser
from xml.etree.ElementTree import Element, SubElement, ElementTree, fromstring
from pathlib import Path
import re
import mat

main_xml = '''
    <geoxfem>
        <configuration name="example" method="FEM" unknown="Displace+Biot" displace_pattern="Standard+Signeded+Branch" equation="Static" solver="Implicit" load="Incremental" />
        <io breakpoint="">
            <log log_level="info" detail_file_name="" />
            <output path="" />
        </io>
        <calculation paralleled="true" gravity_scheme="k0">
            <time_step theta="1" large_defomation="false" />
            <iteration max_time="100" nonlinear_scheme="initial_stress">
                <tolerance relative="0.01" absolute="0" />
            </iteration>
            <xfem active="true" branch_enrich_radius="0">
                <crack_segment default_delta_length="0.03" gauss_num="4" />
                <sif_integral>
                    <domain shape="circle" radius="9" />
                    <qfunction exponent="1" />
                </sif_integral>
            </xfem>
            <water_density value="10" unit="kN\m^3" />
            <atm value="100" unit="kPa" />
        </calculation>
        <topology shape_function_degree="1" elem_nodes_order="clockwise">
            <elem_gauss_num>
                <Segment nGP="3" />
                <Triangle nGP="16" />
                <Quadrangle nGP="9" />
                <Tetrahedral nGP="4" />
                <Tri_prism nGP="6" />
                <Octahedral nGP="8" />
            </elem_gauss_num>
            <cracks />
            <nodes />
            <elements />
        </topology>
        <materials />
        <init_condition>
            <init_displacement />
            <init_pore />
        </init_condition>
        <phases />
    </geoxfem>
'''
phase_xml = '''
    <phase start_time="0" end_time="10" delta_time="10" element_num="9">
        <boundary_condition>
            <dirichlet />
            <neumann />
        </boundary_condition>
    </phase>
'''
mat_xml = {
    mat.elastic.name: mat.elastic.xml,
    mat.DuncanChang.name: mat.DuncanChang.xml,
    mat.CamClay.name: mat.CamClay.xml,
    mat.DruckPrager.name: mat.DruckPrager.xml
}


def indent(elem, level=0):
    i = "\n" + level * "\t"
    if len(elem):
        if not elem.text or not elem.text.strip():
            elem.text = i + "\t"
        if not elem.tail or not elem.tail.strip():
            elem.tail = i
        for elem in elem:
            indent(elem, level + 1)
        if not elem.tail or not elem.tail.strip():
            elem.tail = i
    else:
        if level and (not elem.tail or not elem.tail.strip()):
            elem.tail = i


class gmsh2xml(gmsh_parser):
    def __init__(self):
        gmsh_parser.__init__(self)
        self.root = fromstring(main_xml)

    def writeMesh(self):
        node_num = len(self.m_domain.node_list)
        elem_num = len(self.m_domain.elem_list)
        nodes_root = self.root.find("topology").find("nodes")
        nodes_root.set("num", str(node_num))
        elems_root = self.root.find("topology").find("elements")
        elems_root.set("num", str(elem_num))
        for elem in self.m_domain.elem_list:
            elems_root.append(self.elem2xml(elem))
        for node in self.m_domain.node_list.values():
            nodes_root.append(self.node2xml(node))

    def writeMaterials(self):
        root = self.root.find("materials")
        for mat_id, mat_type in self.m_mats.items():
            new_mat_xml = fromstring(mat_xml[mat_type])
            new_mat_xml.set("id", str(mat_id))
            root.append(new_mat_xml)

    def writeCrack(self):
        new_crack_xml = SubElement(
            self.root.find("topology").find("cracks"), "crack")
        for crack_id, crack in self.m_cracks.items():
            nodes_root = SubElement(new_crack_xml, "nodes")
            node_num = len(crack.node_list)
            nodes_root.set("num", str(node_num))
            elems_root = SubElement(new_crack_xml, "elements")
            elem_num = len(crack.elem_list)
            elems_root.set("num", str(elem_num))
            for elem in self.m_domain.elem_list:
                elems_root.append(self.elem2xml(elem))
            for node in self.m_domain.node_list.values():
                nodes_root.append(self.node2xml(node))

    def writeBoundary(self):
        for phase_id, phase in self.m_phases.items():
            new_phase_xml = fromstring(phase_xml)
            new_phase_xml.set("id", str(phase_id))
            self.root.find("phases").append(new_phase_xml)
            for bcnode in phase.m_dirichlet.node_list.values():
                new_phase_xml.find("boundary_condition").find(
                    "dirichlet").append(self.bcnode2xml(bcnode))
            for bcnode in phase.m_neumann.node_list.values():
                new_phase_xml.find("boundary_condition").find(
                    "neumann").append(self.bcnode2xml(bcnode))

    def save(self, path_str=""):
        if path_str:
            path = Path(path_str)
            assert (path.suffix == ".xml")
        else:
            path = self.m_filename.with_suffix(".xml")
        print("\n5. file is saved to ", path)
        indent(self.root)
        tree = ElementTree(self.root)
        tree.write(path, encoding="utf-8", xml_declaration=True)

    def node2xml(self, node):
        root = Element("vertex")
        root.set("id", str(int(node.id)))
        root.set("x0", str(node.x))
        root.set("x1", str(node.y))
        root.set("x2", str(node.z))
        self.m_node_num += 1
        return root

    def bcnode2xml(self, bcnode):
        root = Element("bcnode")
        root.set("node", str(bcnode.id))
        root.set("dof", str(bcnode.dof))
        root.set("type", "Linear")
        root.set("start", str(bcnode.svalue))
        root.set("end", str(bcnode.evalue))
        return root

    def elem2xml(self, elem):
        root = Element("elem")
        root.set("id", str(self.m_elem_num))
        self.m_elem_num += 1
        root.set("type", str(gmsh_parser.m_elem_types[elem.type]))
        matchObj = re.match(".*mat(\\d+)_.*", elem.info)
        root.set("mat", matchObj.group(1))
        for index in range(len(elem.plist)):
            root.set("v" + str(index), str(int(elem.plist[index])))
        return root


if (__name__ == "__main__"):
    obj = gmsh2xml()
    obj.importGmsh("example.msh")
    obj.write()
